当我们谈论监控系统时,看看Twitter怎么做
Twitter的监控工程团队为我们的内部工程团队提供了全栈的库和多个服务,来监控服务的健康状态、遇到问题发出警告、通过提供分布式系统的调用踪迹来支持肯本原因的调查以及通过创建了一个聚合应用/系统日志的可搜索索引支持诊断。监控工程团队的主要工作包含四个方面:
监视
警告/可视化
分布式系统追踪框架
日志聚合/分析
我们为运行在我们自己拥有和操作的数据中心,以及部署在Amazon AWS和Google Compute等外部公有云中的所有应有和服务都实现了这些功能。
Twitter服务每天处理大量的推送。由于负责提供针对支撑Twitter的服务的可视化功能,我们的监控服务可称的上是最大规模的内部系统。自从我们在两年前的一篇博客中讨论以后,请求的量以及硬盘中的数据容量都已经大大增加。现在,我们的时间序列量度获取服务每分钟处理超过28亿的写请求、存储4.5PB的时间序列数据和处理25000个查询请求。
我们的移动架构一再过去两年中发生了一些变化。以下就是我们现在的系统架构:
量度可以通过三种方式进入我们的监控框架。大部分的Twitter服务都运行一个Python搜集代理,来将量度放入时间序列数据库和HDFS。监控团队也支持一个普通StatsD服务器的简单修改版——Statsite,来放松度量到我们的主时间序列度量获取服务Cuckoo-Write或像Carbon这样的其他获取服务。最后,在获取的便捷性比性能更重要的地方,监控团队提供了一个HTTP API。通过这些方法,我们能够支持来自运行在Twitter数据中心的Twitter服务器或为了利用中心化的监控栈而运行在外部AWS等数据中心的其他公司的大量客户。
所有的Twitter量度都被发送或存储在Twitter的时间序列数据库——Cuckoo。它实际上是有Twitter的分布式键值存储系统Manhattan所支持的两个服务——Cuckoo-Read和Cuckoo-Write。刚开始,Cuckoo-Read和Cuckoo-Write曾是一个服务。但是,由于读写负载的不同特性,他们被划分为了两个服务,使得每一个服务都只实现特定的任务。
Cuckoo-Write是量度的获取点,并暴露了一个API用于写量度。除了将这些量度存储在Manhunttan,Cuckoo-Write还保证这些量度被发送到正确的服务进行索引。最近两周的数据会以分钟的粒度进行存储,而其余的数据以小时的粒度。
时间序列数据库查询引擎Cuckoo-Read处理来自我们的警告和仪表盘系统的时间序列数据查询以及用户初始化的特定查询流。查询以Cuckoo查询语言CQL进行指定。
给定我们数据集的规模,保证低延迟和高可用性的情况下服务所有的查询是非常具有技术难度的。每天,超过3600万个查询是实时执行的,而我们的工程师依靠这些查询和监控系统来满足他们的服务SLA。
Cuckoo-Read原本就支持CQL查询。查询引擎由三个组件构成:解析器、重写器和执行器。解析器负责将查询字符串解析为内部的抽象语法树(Abstract Syntax Tree,AST);然后,重写器处理AST节点,并利用更简单的计算来替换其中一部分节点以改善性能;最后,执行器从下游服务中提取数据并计算结果。
在CQL,时间学习通过一个包含服务、源和量度的数组进行唯一标识。该结构不仅直接映射了服务的组织方式,也使得我们可以简化存储中的数据划分。
我们的时间序列数据库基于服务所在的组或以小时和天的粒度支持数据聚合。过去,我们使用Manhattan计数器来完成基于时间的聚合。我们观察到了两个在每小时和每天数据中的通用的访问模式,从而帮助我们重新设计我们的聚合流水线。这两个访问模式就为:
数据访问通常发生在小时/天边界的回滚之后(例如,由于数据收集发生在晚上9点到10点,大部分的每小时数据读访问都发生在晚上10点以后)。
每小时数据的延迟要求比每分钟数据的要求要低很多。用户通常对每小时数据拥有更高的延迟容忍度。
在这些观察的基础上,我们针对新的聚合流水线,做出以下选择:
将相对昂贵的基于Manhattan计数器的聚合替换为低消耗、高延迟的Hadoop批处理流水线
针对聚合结果采用高密度存储(磁盘 vs SSD)
在来自Hadoop流水线的数据可用之前,对于不经常出现的新进请求从每分钟数据中同步每小时/每天的数据。
利用这一新的流水线,我们以更低的硬件代价取得了重大的效率提升。而且,我们还通过减少时间序列数据库中的负载,改善了系统的可靠性。
时间集索引服务Nuthatch保留着量度元数据的踪迹,并维护一个量度键到成员集的映射(例如,从宿主组到每个宿主的映射)和时间戳。下面的浏览工具显示了一个数据的用例。工程师可以快速导航这些服务、源以及服务中可用的量度。
更重要的是,Nuthatch提供了成员关系解析和CQL查询引擎的单个查询支持,使得工程师能够针对给定的键查询成员关系,然后利用sum()和avg()等函数将所有的时间序列聚合在一起。在下面的查询例子中,Nuthatch负责识别所有的nuthatch.router服务以及在“/update/request/”范围内的源,并提供CQL查询引擎的数据以提取存储中特定时间序列的数据集。
Nuthatch服务的挑战来自即将被索引的流入量度集和来自CQL查询引擎的成员请求的巨大的数据量。一个通用的索引或缓冲引擎在这种情况下并不适用——时间集数据拥有以下特性:
时间序列数据的写会引起大量成员集的更新。请求的量如此之大,以至于Manhattan存储系统不能有效的进行处理。
几乎所有的时间集都会经常被更新,使得基于最近更新的缓冲不再高效。
在一个成员集内的大部分成员,即使一些成员非常高频率的加入或离开,仍然保持相对稳定。
Nuthatch使用一个中间缓存来减少存储操作的数量,以改善性能和降低开销。在该设计中,一个无状态的路由器服务负责接收进行的请求,并决定请求应该被转发到哪些worker碎片以使用兼容的随机。一个带有自身in-memory缓存的worker碎片的集合通过从缓存或Manhattan存储中读取数据来处理请求。
虽然收集和存储数据非常重要,数据对我们工程师而言仍然没有任何意义,除非它被可视化呈现使得工程师能够马上识别出其中的关联。工程师使用CQL查询语言来在一个浏览器内绘制时间序列数据的图表。在一个监控产品中,一个图标是最基本也是肯本性的可视化单元。图表通常称会被嵌入或组织进仪表盘中,但它也可以进行特殊创建,以在执行一个部署或诊断事故的时候能够快速分享信息。此外,工程师可以使用的还包括一个创建仪表盘所使用的命令行工具、可重用监控组件的库以及一个自动化所用的API。
我们通过统一可视化和警告的配置改善了用户监控数据的认知模型。警告只是应用于与可视化和诊断所使用的相同的时间序列数据的预测。由于所有相关的数据都在一个地方,这使得工程师们可以更加容易的解释服务的状态。
仪表盘和图表携带了很多工具来帮助工程师深入了解他们所包含的量度。它们可以利用栈来改变数据的排列和表示方式,填充选项;它们可以在线性和log图表尺度间切换;它们还可以选择不同的时间粒度(每分钟、每小时或每天)。此外,当数据进入流水线或潜回历史数据时,工程师还可以选择近乎实时的查看。在不同的办公室,这些仪表盘就出现在大屏幕或一个工程师的显示器上。Twitter的工程师就生活在这些仪表盘中。
在可视化的用例的,每个仪表盘可以包含上百个图表,而每个图表又可能包含上千个数据点。为了满足所需的浏览器图表性能,我们开发了一个内部使用的的图表库。
当服务降级或损坏时,我们的警告系统将情况告诉给工程师。为了使用警告,工程师在量度上设置了条件。当这些条件满足时,我们提醒工程师。
据评估,警告系统每分钟处理超过25000个警告。考虑到可扩展性和在节点失效时失效备援的冗余性,警告评估被划分到不同的盒子中。
尽管我们之前的系统能够很好的服务Twitter,我们还是迁移到了一个新的分布式警告系统中。这带来的额外好处有:
在区域失效时的跨数据中心的警告失效备援
在节点失效时的警告评估
警告执行隔离,使得一个错的警告并不会影响其他的警告
无影响的部署,使得用户不会损失可视能力
用于警告和可视化配置的统一对象模型
可视化服务允许工程师与警告系统进行交互,并为查看警告状态、参考运行手册以及暂停警告等行动提供了一个UI。
随着我们的系统变得越来越复杂,我们需要一个轻量级的机制来将配置变化配置到大量的服务器中,使得我们能够在不重启服务的情况下快速替换为开发进程的一部分。我们的动态配置库提供了一种部署和升级针对Mesos/Aurora服务及部署在专门机器上的服务的配置的标准方法。该库采用Zookeeper作为配置的信任源。我们使用一个命令行工具来解析配置文件,并在Zookeeper内更新配置数据。依赖该数据的服务会在几秒内收到有关变化的一个通知:
由于我们团队的工程师数量有限,我们曾想加入日益壮大的、致力于OSS Twitter Zipkin的Zipkin开源社区来加速我们的开发速度。结果,监控团队决定通过Open Zipkin项目来彻底开源Zipkin。从此,我们开始与开源社区合作建立控制和框架模型以保证改变能够被规律性的评阅、合并和发布。这些模型被证明非常好:380个下拉请求已经在8个月内被合并到70个社区推动的版本中。所有的文档和通信也都起源于Open Zipkin社区。未来,Twitter会将来自Open Zipkin项目的zipkin版本直接部署到产品环境中。
LogLens是一个提供服务日志的索引、检索、可视化以及分析的服务。它的灵感来源于存在于开发者在运行Aurora/Mesos上的服务时的感受中的两个特殊的差距。
服务日志的生命与任务被调度到的瞬态资源容器的生命之间的耦合导致了我们在针对日志丢失引起的事故进行分流时的很多不确定。
在快速检索构成一个服务的很多组件所产生的所有独特日志时的困难增加了实时事故的反应时间。
Loglens服务的优先级从高到低排序如下:部署的简易性,实时日志的可用性,花销,旧日志的可用性和在有限的开发者投资的情况下可靠操作服务的能力。
客户可以通过一个自助的入口来部署它们的服务。该入口为他们能力和突发头空间有限的服务日志提供了一个索引。日志可以保存在HDFS 7天,一个缓存节点实时服务最近24小时的日志,而更老的日志按需提供。
随着Twitter和监控技术的发展,服务拥有着希望看到他们对我们平台的使用情况。我们将所有的读写请求保存到了Cuckoo,然后使用它们去计算一个简单的利用率量度——读/写率。这一追踪数据也可以用于我们的发展规划和能力规划中。
我们的数据流水线以天为单位来聚合时间数据,而且我们将输出存储在HDFS和Vertica中。我们的用户可以通过三种不同的方式来访问这些数据。首先,我们会给每个团队发送周期性的使用和利用报告。其次,用户可以利用Tableau将Vertica数据进行可视化,从而允许他们对数据进行深入分析。最后,我们还提供了一个带有详细可执行建议的Utilization API。除了显示基本的利用情况和使用情况的数字,该API还被设计为可帮助用户发觉哪些特定组的量度还没有被使用。
由于该想法已经发挥作用,这些工具已经允许用户利用两种方法来跨越读和写之间的缝隙——通过仅仅减少他们写的未使用的量度的数量或通过将单独的量度替换为聚合的量度。结果,一些团队已经能够将其量度的数量减少一个量级。
量度收集中的“推”vs“拉”:在我们编写上次博客的时候,我们的所有量度都是从我们的收集代理中“拉”下来的。我们发现了两个问题:
没有简单的方法可以区分服务失效和收集代理失效这两种情况。服务响应超时和丢失的收集请求都表现为空的时间序列。
我们的收集流水线缺少服务质量隔离。针对各种服务,很难设定一个最优的收集超时的阈值。一个服务的收集时间比较长可以引起使用相同收集代理的其他服务的延迟。
在这种情况下,我们将收集模型从“拉”换为了“推”,增加了我们的服务隔离。每一个宿主上的收集代理只收集运行在该宿主上的服务的量度。此外,除了由服务发出的量度,每一个收集代理分别发送收集的状态追踪量度。
通过这些改变,我们看到了明显的收集可靠性的改善。然而,随着我们转向自助的“推”模型,计划请求的增长情况变得更加困难了。为了解决该问题,我们计划实现服务定额以解决不可预测/没有限制的增长。
容错:作为Twitter最关键服务的其中一种,我们需要在即使发生数据中心失效等灾难性故障时,也能够提供高可用的监控服务。为了达到该目标,我们遵循了两个原则:
跨DC的冗余:一些最关键的量度发送到不止一个数据中心中进行冗余备份。这使得我们可以对抗单数据中心失效的情况。
消除/解耦合对其他库/服务的非必需的依赖:在我们的有些开发过程中,我们有意的移除了对一些广泛使用的Twitter Front End、TFE等内部框架的依赖,以避免这些系统失效引起的宕机事件。在其他情况下,我们使用Manhattan和Zoo专门的集群和实例来将我们的失效和我们所监控服务的失效解耦合开来。
经平台同意授权转载
来源:大数据杂谈 订阅号
作者:张天雷
原文链接:http://mp.weixin.qq.com/s?__biz=MzA5NzkxMzg1Nw==&mid=2653159306&idx=1&sn=fcd981b132d5557b08e88564317d2c44&scene=1&srcid=0510pA95Mn7dDRW4sga7MNaz#wechat_redirect